/*
 * Copyright (c) 2011 Samuel Ventura All Rights Reserved
 */
package dev2net;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 *
 * @author samuel
 */
class ControlThread extends Thread {

    private static int count = 0;
    
    private SharedState sharedState;
    private Socket client;
    private BufferedReader reader;
    private BufferedWriter writer;

    public ControlThread(SharedState sharedState, Socket client) throws IOException {
        this.sharedState = sharedState;
        this.client = client;
        this.reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        this.writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        this.setDaemon(true);
        this.setName("CONTROL" + count++);
    }
    
    @Override
    public void run() {
        try {
            _run();
        } catch (IOException ex) {
            Util.out("ioe:" + ex.getMessage());
        } finally {
            Util.close(client);
            Util.out("exit thread intr:" + Thread.currentThread().isInterrupted());
        }
    }

    private void _run() throws IOException {
        String line;
        while ((line = reader.readLine()) != null) {
            line = trim(line);
            if (line.length()>0) {
                Util.out(">" + line);
                try {
                    if (line.startsWith("bind com")) bindCom(line);
                    else if (line.startsWith("unbind com")) unbindCom(line);
                    else if (line.startsWith("bind mmp")) bindMmp(line);
                    else if (line.startsWith("unbind mmp")) unbindMmp(line);
                    else if (line.startsWith("write mmp")) writeMmp(line);
                    else if (line.startsWith("read mmp")) readMmp(line);
                    else if (line.startsWith("list com")) listCom();
                    else if (line.startsWith("list mmp")) listMmp();
                    else er("Unknown command");
                } catch(IOException ex) {
                    //do not write to er here since socket may be closed
                    ex.printStackTrace();
                }
            }
        }
    }
    
    private void ok() throws IOException {
        Util.out("<OK");
        writer.write("<OK");
        writer.newLine();
        writer.flush();
    }

    private void er(String s) throws IOException {
        Util.err("<ER " + s);
        writer.write("<ER ");
        //ex.getMessage() may return null
        writer.write(s==null? "null" : s);
        writer.newLine();
        writer.flush();
    }

    private void string(String s) throws IOException {
        Util.out(s);
        writer.write(s);
        writer.newLine();
        writer.flush();
    }

    private String trim(String line) {
        //remove external spaces
        line = line.trim();
        //remove internal double spaces
        while (line.indexOf("  ") >= 0) {
            line = line.replaceAll("  ", " ");
        }
        return line;
    }

    private synchronized void bindCom(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=3) throw new RuntimeException("invalid format");
        ComConfig comConfig = new ComConfig(parts[2]);
        Util.close(sharedState.comThreads.remove(comConfig.getPortname()));
        ComThread comThread = new ComThread(comConfig);
        comThread.start();
        sharedState.comThreads.put(comConfig.getPortname(), comThread);
        ok();
    }

    private synchronized void unbindCom(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=3) throw new RuntimeException("invalid format");
        Util.close(sharedState.comThreads.remove(parts[2]));
        ok();
    }

    private synchronized void bindMmp(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=3) throw new RuntimeException("invalid format");
        MmpConfig mmpc = new MmpConfig(parts[2]);
        
        MmpConfig old = sharedState.mmpConfigs.remove(mmpc.getName());
        if (old != null) old.getMmuConfig().remove(old);

        MmuConfig mmuc = sharedState.mmuConfigs.get(mmpc.unitString());
        if (mmuc == null) {
            mmuc = new MmuConfig(mmpc.getIp(), mmpc.getPort());
            sharedState.mmuConfigs.put(mmpc.unitString(), mmuc);
        }
        sharedState.mmpConfigs.put(mmpc.getName(), mmpc);
        mmuc.add(mmpc);        
        ok();
    }

    private synchronized void unbindMmp(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=3) throw new RuntimeException("invalid format");
        String name = parts[2];
        MmpConfig old = sharedState.mmpConfigs.remove(name);
        if (old != null) old.getMmuConfig().remove(old);
        ok();
    }

    private synchronized void writeMmp(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=4) throw new RuntimeException("invalid format");
        String name = parts[2];
        float value = Float.parseFloat(parts[3]);
        MmpConfig mmpConfig = sharedState.mmpConfigs.get(name);
        if (mmpConfig == null) throw new RuntimeException("point not found " + name);
        mmpConfig.write(value);
        ok();
    }

    private synchronized void readMmp(String line) throws IOException {
        String[] parts = line.split(" ");
        if (parts.length!=3) throw new RuntimeException("invalid format");
        String name = parts[2];
        MmpConfig mmpConfig = sharedState.mmpConfigs.get(name);
        if (mmpConfig == null) throw new RuntimeException("point not found " + name);
        string(Float.toString(mmpConfig.read()));
    }

    private synchronized void listCom() throws IOException {
        for(String key : sharedState.comThreads.keySet()) {
            ComThread comt = (ComThread)sharedState.comThreads.get(key);
            ComConfig comc = comt.getConfig();
            string(String.format("com:%s,%d,%d,%d,%s,%d", key,comc.getTcpport(),
                    comc.getBaudrate(),comc.getDatabits(),comc.getParity(),comc.getStopbits()));
        }
    }

    private synchronized void listMmp() throws IOException {
        for(String key : sharedState.mmpConfigs.keySet()) {
            MmpConfig mmpc = sharedState.mmpConfigs.get(key);
            string(String.format("mmp:%s,%s,%d,%d", key,mmpc.getIp(),
                    mmpc.getPort(),mmpc.getIndex()));
        }
        for(String key : sharedState.mmuConfigs.keySet()) {
            MmuConfig mmuc = sharedState.mmuConfigs.get(key);
            string(String.format("mmu:%s,%d", key,mmuc.getPointCount()));
        }
    }
}
